package com.sevennotes.classtable.ui.pages.main_screen

import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.cachedIn
import androidx.paging.filter
import com.google.gson.GsonBuilder
import com.sevennotes.classtable.config.MIN_TIME_MINUTE
import com.sevennotes.classtable.config.Settings
import com.sevennotes.classtable.data.class_items.ClassesRespository
import com.sevennotes.classtable.model.*
import com.sevennotes.classtable.utils.TimeUtil
import com.sevennotes.classtable.utils.WebdavTool
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.roundToInt

data class MainScreenState(
  val classList: LiveData<List<ClassItemWithName>>? = null,
) {
  val minTimeHeight: Float
    get() = (Settings.heightPercent + 1f) * Settings.minTimeHeight
  val oneHourHeight: Int
    get() = (minTimeHeight * (60 / MIN_TIME_MINUTE)).roundToInt()
  val oneDayWidth: Int
    get() = (Settings.minDayWidth * (Settings.widthPercent + 1f)).roundToInt()

  fun sortClassList(): List<ClassItemWithName> {
    return classList?.value?.sortedWith(
      compareBy(
        { it.classItem.weekDay },
        { it.classItem.startTime.startHour },
        { it.classItem.startTime.startMin },
      )
    ) ?: emptyList()
  }

  //根据duration计算课程在课表上的高度
  fun calculateClassHeight(duration: Int): Float {
    val delta = if (duration < 60) 1 else 2
    return (duration / MIN_TIME_MINUTE * minTimeHeight) + delta
  }

  //用于计算课程在课表中的Y坐标
  fun calculatePositionY(time: Time): Int {
    return oneHourHeight * (time.startHour - Settings.beginHour) + (time.startMin / MIN_TIME_MINUTE * minTimeHeight).roundToInt() + (time.startHour - Settings.beginHour)
  }

  // 对给定的高度, 计算新的时间点
  fun calculateNewTime(oldTime: Time, deltaHeight: Int): Time {
    val deltaMinute = (deltaHeight / minTimeHeight).roundToInt() * MIN_TIME_MINUTE
    val newTime = TimeUtil.addTime(oldTime, deltaMinute)
    if (newTime.startHour < Settings.beginHour) {
      newTime.startHour = Settings.beginHour
      newTime.startMin = 0
    }
    return newTime
  }

  //delta: 横向坐标变化量, 返回新的星期数
  fun calculateNewWeekDay(oldWeekDay: Int, delta: Int): Int {
    val weekChange = (delta.toFloat() / oneDayWidth).roundToInt()
    val newWeekDay = oldWeekDay + weekChange
    return when {
      newWeekDay > 6 -> 6
      newWeekDay < 0 -> 0
      else -> newWeekDay
    }
  }
}

data class SaveClassRecordState(
  val recordList: List<ClassRecordState> = emptyList(),
  val currentDate: Date = Date()
)

data class EditStudentState(
  val student: StudentItem = StudentItem(-1, "", gradePoint = 0),
  var classList: List<ClassItem> = emptyList(),
  val deletedClassId: MutableList<Int> = mutableListOf()
) {
  fun sortClassList() {
    classList = classList.sortedWith(
      compareBy(
        { it.weekDay },
        { it.startTime.startHour },
        { it.startTime.startMin },
      )
    )
  }
}

data class GpState(
  val studentId: Int = -1,
  val gpChangeList: LiveData<List<GradePointRecord>>? = null,
  val editable: MutableState<Boolean> = mutableStateOf(false)
)

//class amount record page state
data class CaState(
  val studentId: Int = -1,
  val caChangeList: LiveData<List<ClassAmountRecord>>? = null,
  val editable: MutableState<Boolean> = mutableStateOf(false)
)

data class StudentHistoryState(
  val studentId: Int = -1,
  val historyList: List<ClassRecord> = emptyList(),
)

data class ClassItemState(
  val item: ClassItemWithName,
  val positionY: Int = 0,
  val classHeight: Float = 0f
)

data class ClassRecordState(
  val classItem: ClassItemWithName,
  val gpChange: Int,
  val note: String,
  val shouldSave: Boolean = true
)

data class RecordPageState(
  val from: Date = TimeUtil.currentYear(),
  val to: Date = TimeUtil.todayLast(),
  val searchStr: String = "",
  val total: Int = 0,
  val allRecordedYears: List<String> = emptyList(),
  val selectedRecordYears: List<String> = emptyList(),
  val selectedRecordMonths: List<String> = emptyList(),
  var editable: MutableState<Boolean> = mutableStateOf(false),
)

data class StudentsListState(
  val studentList: LiveData<List<StudentItem>>? = null,
  var editable: MutableState<Boolean> = mutableStateOf(false)
)

data class TableNameListState(
  val editable: MutableState<Boolean> = mutableStateOf(false),
  val currentTable: Int = 0,
  val tableNames: LiveData<List<TableName>>? = null
)

data class SettingsState(
  val busy: Boolean = false,
  var backupFileList: List<String> = emptyList(),
  var autoBackupFileList: List<String> = emptyList()
)

class MainViewModel(private val classesRespository: ClassesRespository) : ViewModel() {

  private val _mainScreenState = MutableStateFlow(MainScreenState())
  val mainScreenState = _mainScreenState.asStateFlow()

  private val _editStudentState = MutableStateFlow(EditStudentState())
  val editStudentState = _editStudentState.asStateFlow()

  private val _studentsListState = MutableStateFlow(StudentsListState())
  val studentsListState = _studentsListState.asStateFlow()

  private val _tableNameListState = MutableStateFlow(TableNameListState())
  val tableNameListState = _tableNameListState.asStateFlow()

  private val _saveClassRecordState = MutableStateFlow(SaveClassRecordState())
  val saveClassRecordState = _saveClassRecordState.asStateFlow()

  private val _recordPageState = MutableStateFlow(RecordPageState())
  val recordPageState = _recordPageState.asStateFlow()

  private val _gpState = MutableStateFlow(GpState())
  val gpState = _gpState.asStateFlow()

  private val _caState = MutableStateFlow(CaState())
  val caState = _caState.asStateFlow()

  private val _settingsState = MutableStateFlow(SettingsState())
  val settingsState = _settingsState.asStateFlow()

  private val _studentHistoryState = MutableStateFlow(StudentHistoryState())
  val studentHistoryState = _studentHistoryState.asStateFlow()

  private val _testPagingSource = MutableStateFlow<PagingSource<Int, ClassRecordWithName>?>(null)

  private val _classRecordStatisFlow by lazy {
    classesRespository.getClassRecordMonthStatistics().map {
      it.map { record -> record.copy(month = record.month.toInt().toString()) }
    }
  }

  val classRecordStatisFlowWithFilter by lazy {
    _classRecordStatisFlow
      .map {statisList ->
        statisList.groupBy { it.year }.toList()
      }
  }

  @SuppressLint("SimpleDateFormat")
  private fun Date.toYearString(): String {
    val yearFormat = SimpleDateFormat("yyyy")
    return yearFormat.format(this)
  }

  @SuppressLint("SimpleDateFormat")
  private fun Date.toMonthString(): String {
    val yearFormat = SimpleDateFormat("M")
    return yearFormat.format(this)
  }

  val classRecordFlow: Flow<PagingData<ClassRecordWithName>> by lazy {
    Pager(PagingConfig(10)) {
      classesRespository.getAllClassRecord()
    }.flow
      .cachedIn(viewModelScope)
      .combine(_recordPageState) { pageData, recordState ->
        pageData.filter {
          recordState.selectedRecordYears.contains(it.classRecord?.startTime?.toYearString())
                  && it.studentName?.contains(recordState.searchStr) ?: true
        }.filter {
          recordState.selectedRecordMonths.contains(it.classRecord?.startTime?.toMonthString())
        }
      }
  }

  init {
    refreshAll()
  }

  fun testGetPagingSource() {
    _testPagingSource.update {
      classesRespository.getAllClassRecordOld(
        startTime = _recordPageState.value.from,
        toTime = _recordPageState.value.to,
        studentName = _recordPageState.value.searchStr
      )
    }
  }

  //测试api
  fun testGetRecordsByyear() {
    val crs = classesRespository.getClassRecordByYears(listOf("2023", "2022", "2021"))
    Log.d("test", "getclass size: ${crs.size}")
    crs.forEach {
      Log.d("test", "class: $it")
    }
  }

  fun testAddClassRecord() {
    viewModelScope.launch {
      repeat(5) {
        classesRespository.addStudent(
          StudentInfo(
            studentItem = StudentItem(name = "张$it")
          )
        )
      }
      repeat(100) {
        classesRespository.insertClassRecord(
          ClassRecord(
            studentId = Random().nextInt(5),
            gpChange = 3,
            startTime = TimeUtil.radomDate(),
            duration = 40,
            note = "测试"
          )
        )
      }
    }
  }

  //----------------------MAIN   SCREEN    API-----------------------------
  fun onClassItemMoved(classItem: ClassItemWithName, xChange: Int, yChange: Int) {
    viewModelScope.launch {
      with(_mainScreenState.value) {
        val newWeekDay = calculateNewWeekDay(classItem.classItem.weekDay, xChange)
        val newTime = calculateNewTime(classItem.classItem.startTime, yChange)
        val newClassItem =
          classItem.classItem.copy(weekDay = newWeekDay, startTime = newTime)

        classesRespository.updateClassItem(newClassItem)
      }
    }
  }

  private fun refreshAll() {
    viewModelScope.launch {
      val count = classesRespository.getTableCount()
      var tableId: Int
      if (count == 0) {
        tableId = classesRespository.addTableName(TableName(name = "默认课表")).toInt()
        Settings.currentTableNameId = tableId
      } else {
        tableId = Settings.currentTableNameId
      }
      if (tableId == -1) {
        tableId = classesRespository.getFirstTableName().id!!
        Settings.currentTableNameId = tableId
      }
      val tableNames = classesRespository.getAllTableNames()
      updateClassList(tableId)
      _tableNameListState.update {
        it.copy(currentTable = tableId, tableNames = tableNames)
      }
      _studentsListState.update {
        it.copy(
          studentList = classesRespository.getAllStudents()
        )
      }
    }
  }

  private suspend fun updateClassList(tableId: Int) {
    val items = classesRespository.getClassItems(tableId)
    _mainScreenState.update {
      it.copy(classList = items)
    }
  }

//----------------------_EDIT STUDENT API-------------------------

  fun refreshEditStudentState(studentId: Int?) {
    viewModelScope.launch {
      if (studentId == null) {
        _editStudentState.update {
          it.deletedClassId.clear()
          it.copy(student = StudentItem(-1, "", gradePoint = 0), classList = emptyList())
        }
      } else {
        updateStateById(studentId)
      }
    }
  }

  private suspend fun updateStateById(studentId: Int) {
    val info = classesRespository.getStudentInfoById(studentId, Settings.currentTableNameId)
    _editStudentState.update {
      it.deletedClassId.clear()
      val newState = it.copy(
        student = info?.studentItem ?: StudentItem(),
        classList = info?.classes ?: emptyList()
      )
      newState.sortClassList()
      newState
    }
  }

  fun editClass(index: Int, weekDay: Int, startTime: Time, duration: Int) {
    _editStudentState.update {
      val newList = it.classList.toMutableList().also { list ->
        list[index] = list[index].copy(
          weekDay = weekDay,
          startTime = startTime,
          duration = duration
        )
      }
      it.copy(classList = newList)
    }
  }

  fun addClass(studentId: Int, weekDay: Int, startTime: Time, duration: Int) {
    _editStudentState.update {
      val newList = it.classList + ClassItem(
        id = -1,
        studentId = studentId,
        weekDay = weekDay,
        startTime = startTime,
        duration = duration,
        tableId = Settings.currentTableNameId
      )
      it.copy(classList = newList)
    }
  }

  fun deleteClass(classIndex: Int) {
    _editStudentState.update {
      it.deletedClassId.add(it.classList[classIndex].id!!)
      val newList = it.classList.toMutableList().apply { removeAt(classIndex) }
      it.copy(classList = newList)
    }
  }

  fun deleteStudent(studentItem: StudentItem) {
    viewModelScope.launch {
      classesRespository.deleteStudent(studentItem)
    }
  }

  fun changeStudentInfo(student: StudentItem) {
    _editStudentState.update {
      it.copy(student = student)
    }
  }

  //添加和编辑学生详情页的apply
  fun applyChanges() {
    viewModelScope.launch {
      with(_editStudentState.value) {
        classesRespository.updateStudent(
          StudentInfo(
            studentItem = student,
            classes = classList
          )
        )
        deletedClassId.forEach {
          classesRespository.deleteClassItem(it)
        }
      }
    }
  }

  //----------------SAVE CLASS RECORD API -----------------------------

  fun refreshSaveClassRecordState(date: Date = Date()) {
    val weekDay = TimeUtil.currentWeekDay(date)
    val classList =
      mainScreenState.value.classList?.value?.filter { it.classItem.weekDay == weekDay }
    val recordList = classList?.map { ClassRecordState(it, 0, "") }?.sortedWith(
      compareBy(
        { it.classItem.classItem.weekDay },
        { it.classItem.classItem.startTime.startHour },
        { it.classItem.classItem.startTime.startMin },
      )
    ) ?: emptyList()
    _saveClassRecordState.update {
      it.copy(
        recordList = recordList,
        currentDate = date
      )
    }
  }

  fun applyClassRecords() {
    viewModelScope.launch {
      //classRecordState
      val crs = saveClassRecordState.value
      val c = Calendar.getInstance()
      c.time = crs.currentDate
      val list = crs.recordList
      list.forEach {
        if (it.shouldSave) {
          c.set(Calendar.HOUR_OF_DAY, it.classItem.classItem.startTime.startHour)
          c.set(Calendar.MINUTE, it.classItem.classItem.startTime.startMin)
          val cr = ClassRecord(
            studentId = it.classItem.classItem.studentId,
            gpChange = it.gpChange,
            startTime = c.time,
            duration = it.classItem.classItem.duration,
            note = it.note
          )
          classesRespository.insertClassRecord(cr)
        }
      }
    }
  }

  fun updateClassRecordState(classRecordState: ClassRecordState) {
    viewModelScope.launch {
      _saveClassRecordState.update {
        it.copy(
          recordList = it.recordList.map { item ->
            if (item.classItem.classItem.id == classRecordState.classItem.classItem.id)
              classRecordState
            else
              item
          }
        )
      }
    }
  }

//---------------TABLE NAME API---------------------------------------

  fun changeCurrentTableName(tableName: TableName) {
    viewModelScope.launch {
      classesRespository.updateTableName(tableName)
    }
  }

  //改变当前课表
  fun changeCurrentTable(tableId: Int) {
    viewModelScope.launch {
      Settings.currentTableNameId = tableId
      updateClassList(tableId)
      _tableNameListState.update { it.copy(currentTable = tableId) }
    }
  }

  fun copyClassesFromOtherTable(tableId: Int) {
    viewModelScope.launch {
      classesRespository.copyClassesToNewTable(tableId, Settings.currentTableNameId)
    }
  }

  fun addTableName(tableName: TableName) {
    viewModelScope.launch {
      val id = classesRespository.addTableName(tableName)
      changeCurrentTable(id.toInt())
    }
  }

  fun deleteTableName(tableName: TableName) {
    viewModelScope.launch {
      classesRespository.deleteTableName(tableName)
      changeCurrentTable(_tableNameListState.value.tableNames?.value?.get(0)?.id ?: 0)
    }
  }

  //-----------RECORD PAGE API------------------------
  fun refreshRecordFlow() {
    viewModelScope.launch {
      _recordPageState.update {
        it.copy(
          total = classesRespository.getClassRecordCount(it.from, it.to, it.searchStr),
          allRecordedYears = classesRespository.getAllStartYears()
        )
      }

    }
  }

  fun onRecordSelectedYearChanged(yearList: List<String>) {
    _recordPageState.update {
      it.copy(selectedRecordYears = yearList)
    }
  }

  fun onRecordSelectedMonthChanged(monthList: List<String>) {
    _recordPageState.update {
      it.copy(selectedRecordMonths = monthList)
    }
  }

  fun changeSearchStr(str: String) {
    viewModelScope.launch {
      _recordPageState.update {
        it.copy(
          searchStr = str,
          total = classesRespository.getClassRecordCount(it.from, it.to, it.searchStr)
        )
      }
      refreshRecordFlow()
    }
  }

  fun changeFromDate(date: Date) {
    viewModelScope.launch {
      _recordPageState.update {
        it.copy(
          from = date,
          total = classesRespository.getClassRecordCount(it.from, it.to, it.searchStr)
        )
      }
      refreshRecordFlow()
    }
  }

  fun changeToDate(date: Date) {
    viewModelScope.launch {
      _recordPageState.update {
        it.copy(
          to = date,
          total = classesRespository.getClassRecordCount(it.from, it.to, it.searchStr)
        )
      }
      refreshRecordFlow()
    }
  }

  fun deleteClassRecord(classRecord: ClassRecord) {
    viewModelScope.launch {
      classesRespository.deleteClassRecord(classRecord)
      _recordPageState.update {
        it.copy(total = classesRespository.getClassRecordCount(it.from, it.to, it.searchStr))
      }
    }
  }

  //-----------__GP STATE API ------------------
  fun refreshGpState(studentId: Int) {
    viewModelScope.launch {
      val list = classesRespository.getGpRecordById(studentId)
      _gpState.update {
        it.copy(studentId = studentId, gpChangeList = list)
      }
    }
  }

  fun addGpRecord(gp: GradePointRecord) {
    viewModelScope.launch {
      classesRespository.insertGradePointRecord(gp)
    }
  }

  fun deleteGpRecord(gp: GradePointRecord) {
    viewModelScope.launch {
      classesRespository.deleteGradePointRecord(gp)
    }
  }

  //----------CLASS AMOUNT RECORD API---------------
  fun refreshCaState(studentId: Int) {
    viewModelScope.launch {
      val list = classesRespository.getClassAmountRecordById(studentId)
      _caState.update {
        it.copy(studentId = studentId, caChangeList = list)
      }
    }
  }

  fun addCaRecord(ca: ClassAmountRecord) {
    viewModelScope.launch {
      classesRespository.insertClassAmountRecord(ca)
    }
  }

  fun deleteCaRecord(ca: ClassAmountRecord) {
    viewModelScope.launch {
      classesRespository.deleteClassAmountRecord(ca)
    }
  }

  fun clearUnexistRecords() {
    viewModelScope.launch {
      classesRespository.clearUnexistRecord()
    }
  }

  //--------------STUDENT HISTORY API------------------------
  fun refreshStudentHistoryState(studentId: Int) {
    viewModelScope.launch {
      val list = classesRespository.getAllClassRecordsByStudentId(studentId)
      _studentHistoryState.update {
        it.copy(studentId = studentId, historyList = list)
      }
    }
  }


  //-------------BACK UP -------------------------

  val gson = GsonBuilder().setDateFormat("MMM dd, yyyy HH:mm:ss").create()

  private fun initWebdav() {
    WebdavTool.initSardine(
      userName = Settings.webDavUsername,
      password = Settings.webDavPassword,
      url = Settings.webDavUrl
    )
  }

  fun refreshBackupFileNames(onError: (String) -> Unit) {
    initWebdav()
    viewModelScope.launch(IO) {
      _settingsState.update {
        it.copy(
          busy = true,
          backupFileList = emptyList(),
          autoBackupFileList = emptyList()
        )
      }
      val list = try {
        WebdavTool.getExistFiles(Settings.dirName).map { it.name }
      } catch (e: Exception) {
        withContext(Main) {
          onError(e.message ?: "获取列表出错")
        }
        emptyList<String>()
      }

      val autoList = try {
        WebdavTool.getExistFiles(Settings.autoSaveDir).map { it.name }
      } catch (e: Exception) {
        withContext(Main) {
          onError(e.message ?: "获取列表出错")
        }
        emptyList<String>()
      }
      _settingsState.update {
        it.copy(
          busy = false,
          backupFileList = list,
          autoBackupFileList = autoList
        )
      }
    }
  }

  fun getBackupFileTest(onError: (String) -> Unit) {
    _settingsState.update { it.copy(busy = true, backupFileList = emptyList()) }
    viewModelScope.launch(IO) {
      delay(2000)
      _settingsState.update {
        it.copy(
          busy = false,
          backupFileList = listOf("${Random().nextInt()}", "${Random().nextInt()}")
        )
      }
    }
  }

  fun onNetRecoverTest(name: String, finish: (String) -> Unit) {
    _settingsState.update { it.copy(busy = true) }
    viewModelScope.launch(IO) {
      delay(2000)
      finish("成功！filename: $name")
      _settingsState.update { it.copy(busy = false) }
    }
  }

  fun netBackupJsonFile(
    fileName: String,
    autoSave: Boolean = false,
    finish: (String) -> Unit
  ) {
    viewModelScope.launch {
      initWebdav()
      launch(IO) {
        _settingsState.update { it.copy(busy = true) }
        val backup = classesRespository.getBackupItem()
        try {
          val json = gson.toJson(backup)
          WebdavTool.uploadByteArray(
            fileName = fileName,
            dirPath = if (autoSave) Settings.autoSaveDir else Settings.dirName,
            data = json.toByteArray()
          )
          withContext(Main) {
            finish("上传成功!")
          }
        } catch (e: Exception) {
          withContext(Main) {
            finish("上传失败! ${e.message}")
          }
        }
        _settingsState.update { it.copy(busy = false) }
      }
    }
  }

  fun netDeleteBackupFile(filePath: String, finish: (String) -> Unit) {
    viewModelScope.launch(IO) {
      _settingsState.update { it.copy(busy = true) }
      initWebdav()
      try {
        WebdavTool.deleteFile(filePath)
      } catch (e: Exception) {
        withContext(Main) {
          finish(e.message ?: "发生错误，删除失败")
        }
      }
      withContext(Main) {
        finish("删除成功")
      }
      _settingsState.update { it.copy(busy = false) }
    }
  }

  fun checkAndDeleteExistSaveFiles() {
    viewModelScope.launch(IO) {
      initWebdav()
      val existFileList = try {
        WebdavTool.getExistFiles(Settings.autoSaveDir)
      } catch (e: Exception) {
        emptyList()
      }
      if (existFileList.size > Settings.maxAutoSaveFileNumber) {
        //文件数量超过最大值，需要清理
        existFileList
          .sortedBy { it.modified }
          .subList(0, existFileList.size - Settings.maxAutoSaveFileNumber)
          .forEach { fileRes ->
            WebdavTool.deleteFile("${Settings.autoSaveDir}/${fileRes.name}")
          }
      }
    }
  }

  @Deprecated("备份功能改为备份为json文件")
  fun netBackupFile(finish: (String) -> Unit) {
    viewModelScope.launch {
      val backup = classesRespository.getBackupItem()
      initWebdav()
      launch(IO) {
        _settingsState.update { it.copy(busy = true) }
        try {
          ByteArrayOutputStream().use { bos ->
            ObjectOutputStream(bos).use { oos ->
              oos.writeObject(backup)
            }
            WebdavTool.uploadByteArray(
              fileName = "backup.kc",
              dirPath = Settings.dirName,
              data = bos.toByteArray()
            )
          }
          withContext(Main) {
            finish("上传成功!")
          }
        } catch (e: Exception) {
          withContext(Main) {
            finish("上传失败! ${e.message}")
          }
        }
        _settingsState.update { it.copy(busy = false) }
      }
    }
  }

  fun netReadFile(filePath: String, finish: (String) -> Unit) {
    initWebdav()
    viewModelScope.launch {
      _settingsState.update { it.copy(busy = true) }
      launch(IO) {
        try {
          val inputs = WebdavTool.readFile(filePath)
          inputs.use { ins ->
            if (filePath.endsWith("json")) {
              val content = ins.bufferedReader().use { it.readText() }
              val backup = gson.fromJson(content, ClassTableBackup::class.java)
              recoveryBackup(backup, null)
            } else {
              ObjectInputStream(ins).use { ois ->
                ois.readObject().also {
                  val backup = it as ClassTableBackup
                  recoveryBackup(backup, null)
                }
              }
            }
          }
          finish("读取成功!")
        } catch (e: Exception) {
          finish("读取失败 ${e.message}")
        }
        _settingsState.update { it.copy(busy = false) }
      }
    }
  }

  fun backupFileJson(fileDir: Uri, context: Context) {
    viewModelScope.launch {
      val backup = classesRespository.getBackupItem()
      try {
        context.contentResolver.openOutputStream(fileDir)?.use { outputStream ->
          val json = gson.toJson(backup)
          outputStream.write(json.toByteArray())
        }
        Toast.makeText(context, "存储成功", Toast.LENGTH_SHORT).show()
      } catch (e: Exception) {
        Toast.makeText(context, "存储失败${e.message}", Toast.LENGTH_SHORT).show()
      }
    }
  }

  fun readBackupFileJson(uri: Uri, context: Context) {
    try {
      context.contentResolver.openInputStream(uri)?.use { inputStream ->
        val content = inputStream.bufferedReader().use { it.readText() }
        val backup = gson.fromJson(content, ClassTableBackup::class.java)
        recoveryBackup(backup, context)
      }
      Toast.makeText(context, "读取成功", Toast.LENGTH_SHORT).show()
    } catch (e: Exception) {
      Toast.makeText(context, "读取失败${e.message}", Toast.LENGTH_SHORT).show()
    }
  }

  fun backupFile(fileDir: Uri, context: Context) {
    viewModelScope.launch {
      val backup = classesRespository.getBackupItem()
      try {
        context.contentResolver.openFileDescriptor(fileDir, "w")?.use {
          FileOutputStream(it.fileDescriptor).use { fos ->
            ObjectOutputStream(fos).use { oos ->
              oos.writeObject(backup)
            }
          }
        }
        Toast.makeText(context, "存储成功", Toast.LENGTH_SHORT).show()
      } catch (e: Exception) {
        Toast.makeText(context, "存储失败${e.message}", Toast.LENGTH_SHORT).show()
      }
    }
  }

  fun readBackupFile(uri: Uri, context: Context) {
    try {
      context.contentResolver.openFileDescriptor(uri, "r")?.use {
        FileInputStream(it.fileDescriptor).use { fis ->
          ObjectInputStream(fis).use { ois ->
            ois.readObject().also { o ->
              val backup = o as ClassTableBackup
              recoveryBackup(backup, context)
            }
          }
        }
      }
    } catch (e: Exception) {
      Toast.makeText(context, "读取失败${e.message}", Toast.LENGTH_SHORT).show()
    }
  }

  private fun recoveryBackup(backup: ClassTableBackup, context: Context?) {
    viewModelScope.launch {
      _settingsState.update { it.copy(busy = true) }
      classesRespository.clearAllDBs()
      classesRespository.recovery(backup)
      _settingsState.update { it.copy(busy = false) }
      context?.let { Toast.makeText(it, "读取成功", Toast.LENGTH_SHORT).show() }
    }
  }

  companion object {
    fun provideFactory(
      classesRespository: ClassesRespository,
    ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
      @Suppress("UNCHECKED_CAST")
      override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MainViewModel(classesRespository) as T
      }
    }
  }
}